<?php
/**
 * Core_Config
 * 
 * Класс для получения кофигурации из настроечных файлов.
 * Пример:
 * 1) users|names - ищет файл users(.php|.ini|.xml по-умолчанию) в конфиг-папках 
 * установленных модулей; потом ищет секцию "names".
 * 2) main/database|:app|adapter -  ищет файл main/database(.php|.ini|.xml по-умолчанию) 
 * в конфиг-папках установленных модулей; далее ищет внутреннюю переменную "app" и ищет 
 * секцию с ее значением; далее ищет секцию "adapter"
 * @author naspeh
 * @version $Id: Config.php 662 2008-06-25 20:16:56Z naspeh $
 */
class Core_Config
{
    /**
     * Опции
     *
     * @var array
     */
    protected static $_options = array(
        'debug' => false,
        'priority' => Zend_Log::CRIT
    );
    /**
     * Модули
     *
     * @var array
     */
    protected static $_modules = array();
    /**
     * Модули по умолчанию
     *
     * @var array
     */
    protected static $_defaultModules = array(Core_Loader::MODULE_DEFAULT);
    /**
     * Метки
     *
     * @var array
     */
    protected static $_labels = array();
    /**
     * Получение конфигурации по метке.
     *
     * @param string $label
     * @param array|string $module
     * @param boolean $zendConfigObj
     * @return mixed
     */
    public static function get($label, $module = null, $zendConfigObj = false)
    {
        self::_securityCheckLabel((string) $label);
        $label = trim($label, '|');
        $labels = preg_split('/\|/', $label);
        $labels = array_diff($labels, array(''));
        if (empty($labels)) {
            throw new Core_Exception('emptyConfigLabel# пустая метка конфигурации');
        }
        if (is_null($module)) {
            $modules = array_merge(self::$_modules, self::$_defaultModules);
        } elseif (is_array($module)) {
            $modules = array_merge($module, self::$_defaultModules);
            $label = $labels[1];
        } elseif (is_string($module)) {
            $modules = array_merge(array($module), self::$_defaultModules);
        } else {
            throw new Core_Exception('wrongParam# неправильный тип параметра');
        }
        $label = self::_replace($labels[0]);
        unset($labels[0]);
        $filename = self::getFile($label, $modules);
        $firstSection = null;
        if (isset($labels[1])) {
            $firstSection = self::_replace($labels[1]);
            unset($labels[1]);
        }
        preg_match('/\.(\w*)$/U', $filename, $matches);
        $type = $matches[1];
        switch ($type) {
            case 'php':
                $config = new Zend_Config(include $filename);
                if (isset($firstSection)) {
                    $config = $config->$firstSection;
                }
                break;
            case 'ini':
                $config = new Zend_Config_Ini($filename, $firstSection);
                break;
            case 'xml':
                $config = new Zend_Config_Xml($filename, $firstSection);
                break;
            default:
                $config = null;
                break;
        }
        if (! is_null($labels)) {
            foreach ($labels as $section) {
                $section = self::_replace($section);
                $config = $config->$section;
            }
        }
        if (!$zendConfigObj && ($config instanceof Zend_Config)) {
            $config = $config->toArray();	
        }
        return $config;
    }
    /**
     * Возвращает путь к конфиг-файлу и его тип по метке.
     *
     * @param string $label
     * @param array $modules
     * @return array
     */
    protected static function getFile($label, array $modules = array())
    {
        if (empty($modules)) {
            $modules = self::getDefaultModules();
        }
        $modules = array_reverse($modules);
        foreach ($modules as $module) {
            if (false == ($path = Core_Loader::getModuleToPath($module))) {
                throw new Core_Exception('moduleNotFoundForLable# модуль "' . $module . '" не зарегистрирован для метки: "' . $label . '"');
            }
            $dir = $path . Core_Loader::SUFFIX_CONFIGS;
            foreach (array_reverse(self::getOptions()->types->toArray()) as $type) {
                $filename = $dir . DIRECTORY_SEPARATOR . $label . '.' . $type;
                if (Zend_Loader::isReadable($filename)) {
                    return $filename;
                }
            }
        }
        throw new Core_Exception('fileNotFoundForLabel# файл не найден для метки:"' . $label . '"');
    }
    /**
     * Добавляет модуль в стек модулей.
     *
     * @param array|string $module
     * @param boolean $toDefault
     */
    protected static function addModule($module, $toDefault = false) 
    {
        $module = (string) $module;
        if (false===Core_Loader::getModuleToPath($module)) {
            throw new Core_Exception('modeuleNotFound# Модуль:"' . $module . '" не зарегистрирован');
        }
        if ($toDefault) {
            self::$_defaultModules[] = $module;
        } else {
            self::$_modules[] = $module;
        }
    }
    /**
     * Очищает действующие модули
     *
     * @return boolean
     */
    public static function clearModules()
    {
        self::$_modules = array();
        return true;
    }
    /**
     * Установка действующего модуля/модулей.
     *
     * @param array|string $modules
     * @return array
     */
    public static function setModules($modules)
    {
        self::clearModules();
        if (is_array($modules)) {
            foreach ($modules as $module) {
                self::addModule($module);
            }
        } elseif (is_string($modules)) {
            self::addModule($modules);
        } else {
            throw new Core_Exception('wrongParam# неправильный тип параметра');
        }
        return self::$_modules;
    }
    /**
     * Возвращает действующие модули
     *
     * @return array
     */
    public static function getModules()
    {
        return self::$_modules;
    }
    /**
     * Устанавливает модули по-умолчанию для поиска конфиг-файлов
     *
     * @param array|string $modules
     * @return array
     */
    public static function setDefaultModules($modules)
    {
        self::clearDefaultModules();
        if (is_array($modules)) {
            foreach ($modules as $module) {
                self::addModule($module, true);
            }
        } elseif (is_string($modules)) {
            self::addModule($modules, true);
        } else {
            throw new Core_Exception('wrongParam# неправильный тип параметра');
        }
        return self::$_defaultModules;
    }
    /**
     * Очищает модули по-умолчанию
     *
     * @return boolean
     */
    public static function clearDefaultModules()
    {
        self::$_defaultModules = array();
        return true;
    }
    /**
     * Возвращает модули установленные по-умолчанию
     *
     * @return array
     */
    public static function getDefaultModules()
    {
        return self::$_defaultModules;
    }
    /**
     * Проверяет на отсутствие запрещенных символов в метке.
     *
     * @param string $label
     */
    protected static function _securityCheckLabel($label)
    {
        if (preg_match('/[^a-z0-9\\/\-_\|:]/i', $label)) {
            throw new Core_Exception('wrongLabel# Не допустимый символ в метке:"' . $label . '"');
        }
    }
    /**
     * Устанавливает внутренние переменные(метки) для использования  в основной конфиг-метке
     *
     * @param string $label
     * @param string $value
     * @return array
     */
    public static function setLabel($label, $value)
    {
        return self::$_labels[(string)$label] = (string) $value;
    }
    /**
     * Возвращает установленую внутреннюю переменную, все, если $label = null.
     *
     * @param string $label
     * @return mixed
     */
    public static function getLabel($label = null)
    {
        if (is_null($label)) {
        	return self::$_labels;
        }
        
        if (isset(self::$_labels[$label])) {
            return self::$_labels[$label];
        }
        
        return false;
    }
    /**
     * Формирует метку с учетом внутренних переменных.
     *
     * @param string $label
     * @return string
     */
    protected static function _replace($label)
    {
        $matches = array();
        if (preg_match('/^:(.*)/', $label, $matches) && self::getLabel($matches[1])) {
            return self::getLabel($matches[1]);
        } else {
            return $label;
        }
    }
    /**
     * Устанавливает опции
     *
     * @param Zend_Config|array $options
     */
    public static function setOptions($options)
    {
        if ($options instanceof Zend_Config) {
            self::$_options = $options->toArray();
        } else {
            self::$_options = $options;
        }
    }
    /**
     * Возвращает опции
     *
     * @return Zend_Config
     */
    public static function getOptions()
    {
        $options = new Zend_Config(self::$_options);
        return $options;
    }        
}
